/*
 DNN Content Builder - sourceforge.net/projects/dnncb
 Copyright (c) 2004-2008
 by Emerson Lopes (emersonlopes@gmail.com)

 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 
 documentation files (the "Software"), to deal in the Software without restriction, including without limitation 
 the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and 
 to permit persons to whom the Software is furnished to do so, subject to the following conditions:

 The above copyright notice and this permission notice shall be included in all copies or substantial portions 
 of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 
 TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 
 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
 DEALINGS IN THE SOFTWARE.
*/

namespace DNNContentBuilder.UserControls
{
	using System;
	using System.Data;
	using System.Text;
	using System.Collections;
	using System.IO;
	using System.Diagnostics;
	using System.Xml;
	using System.Runtime.Serialization.Formatters.Binary;

	using ICSharpCode.SharpZipLib.BZip2;
	using ICSharpCode.SharpZipLib.Zip;
	using ICSharpCode.SharpZipLib.Zip.Compression;
	using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
	using ICSharpCode.SharpZipLib.GZip;

	/// <summary>
	/// Controls the folder used to deploy formlet parts
	/// </summary>
	public enum FormletTargetFolder
	{
		Default,
		AppResources,
		XML,
		FilePath,
		Bin
	}

	public class Unzip
	{
		private string          result = "";
		private string			localPath = "";
		private string          appPath = "";
		private string			binPath = "";
		private ZipInputStream	zip;
		private XmlDocument		xmlDoc	= new XmlDocument();
		private ArrayList		entries = new ArrayList();
		private System.Text.RegularExpressions.Regex regex;

		public string Execute(System.IO.Stream zipFile, System.Web.UI.TemplateControl handler)
		{
			localPath = handler.ResolveUrl("~/DesktopModules/DNNContentBuilder/UserControls/FieldControls/");
			appPath   = handler.ResolveUrl("~/DesktopModules/DNNContentBuilder/");
			binPath   = handler.ResolveUrl("~/bin/");

			zip = new ZipInputStream(zipFile);
			
			FormletInfo formlet = new FormletInfo();

			if (zip.Length == 0)
			{
				return "Invalid Zip File!";
			}

			string xml = "";
		
			ZipEntry entry;
			
			result = result + "<P>";
			
			while ((entry = zip.GetNextEntry()) != null) 
			{
				result = result + entry.Name + "<BR>";

				byte[] buffer = new byte[Convert.ToInt32(entry.Size)];

				int bytesRead = 0;

				while (bytesRead < buffer.Length)
				{
					bytesRead += zip.Read(buffer, bytesRead, buffer.Length - bytesRead);
				}

				// Make sure we read the correct amount of data
				
				try {
					System.Diagnostics.Debug.Assert(bytesRead == buffer.Length, "bytesRead != buffer.Lenth"); 
				}
				catch (Exception ) {
					result = result + "Invalid zip file!<p>";
					return result;
				}

				if (entry.Name.ToLower() == "formlet.xml") 
				{
					if (entry.Size == 0) 
					{
						return "ERROR: formlet.xml ?";
					}

					xml = (new System.Text.ASCIIEncoding()).GetString(buffer);
				} 

				System.Web.UI.Pair pair = new System.Web.UI.Pair(entry, buffer);

				entries.Add(pair);
			}

			if (xml == "") 
			{
				return "ERROR: formlet.xml?";
			}

			xmlDoc.LoadXml(xml);
	
			formlet.Name			= FindNode("//name");
			formlet.Folder			= FindNode("//folder");
			formlet.Assembly		= FindNode("//assembly");
			formlet.ConfigFile		= FindNode("//configFile");
			formlet.ControlAscx		= FindNode("//controlAscx");
			formlet.DefinitionAscx	= FindNode("//definitionAscx");

			XmlElement root = xmlDoc.DocumentElement;

			formlet.Dependencies	= root.SelectNodes("//fileName");
			formlet.Resources       = root.SelectNodes("//resource");

			ControlTypeController ctController	= new ControlTypeController();
			ControlTypeInfo ctInfo				= new ControlTypeInfo();

			ctInfo.AssemblyName = formlet.Assembly;
			ctInfo.Title		= formlet.Name;

			string defaultPath = @"DesktopModules\DNNContentBuilder\UserControls\FieldControls\" + formlet.Folder + @"\";

			ctInfo.ControlPath = defaultPath + formlet.DefinitionAscx;

			// Saving Definition ASCX

			PsychoZipIterator(formlet.DefinitionAscx, formlet, FormletTargetFolder.Default);

			// Saving Control ASCX

			PsychoZipIterator(formlet.ControlAscx, formlet, FormletTargetFolder.Default);

			// Saving XML

			PsychoZipIterator(formlet.ConfigFile, formlet, FormletTargetFolder.XML);

			// Saving Resource files

			if (formlet.Resources != null) 
			{
				PsychoZipIterator(formlet.Resources, formlet, FormletTargetFolder.AppResources);
			}

			// Saving dependencies
			if (formlet.Dependencies != null) 
			{
				PsychoZipIterator(formlet.Dependencies, formlet, FormletTargetFolder.FilePath);
			}

			// Saving assembly
			if (formlet.Assembly != null) 
			{
				PsychoZipIterator(formlet.Assembly + ".dll", formlet, FormletTargetFolder.Bin);
			}

			// before add a new control check if it exists in the database
			if ((ControlTypeInfo)ctController.GetItemByPath(ctInfo.ControlPath) == null) 
			{ 
				ctController.Add(ctInfo);
			}

			return result;
		}

		private void PsychoZipIterator(string file,  FormletInfo formlet, FormletTargetFolder folderType) 
		{
			foreach (System.Web.UI.Pair pair in entries) 
			{
				ZipEntry entry = (ZipEntry)pair.First;
				
				if (entry.Name.ToLower() == file.ToLower()) 
				{
					byte[] data = (byte[])pair.Second;

					string directoryName = "";

					switch (folderType) 
					{
						case FormletTargetFolder.Bin:
						{
							directoryName = System.Web.HttpContext.Current.Server.MapPath(binPath);
							break;
						}

						default:
						{
							directoryName = System.Web.HttpContext.Current.Server.MapPath(localPath);
							break;
						}
					}
					
					WriteFile(SetPath(file, directoryName, folderType, formlet), entry, data);
				}
			}
		}

		private void PsychoZipIterator(XmlNodeList nodeList, FormletInfo formlet, FormletTargetFolder folderType) 
		{
			foreach (System.Web.UI.Pair pair in entries) 
			{
				ZipEntry entry = (ZipEntry)pair.First;

				byte[] data = (byte[])pair.Second;

				string file = MatchFileName(entry.Name, nodeList);

				if (file != null) 
				{
					string directoryName = System.Web.HttpContext.Current.Server.MapPath(localPath);

					WriteFile(SetPath(file, directoryName, folderType, formlet), entry, data);
				}
			}
		}

		private string SetPath(string file, string path, FormletTargetFolder folder, FormletInfo formlet) 
		{
			switch (folder) 
			{
				case FormletTargetFolder.Default:  
				{
					return path + formlet.Folder + "\\" + file;
				}

				case FormletTargetFolder.XML:
				{
					return path + formlet.Folder + "\\" + file;
				}

				case FormletTargetFolder.AppResources:
				{
					return System.Web.HttpContext.Current.Server.MapPath(appPath + @"App_LocalResources\" + file);
				}
			}
			return path + file;
		}

		private bool WriteFile(string path, ICSharpCode.SharpZipLib.Zip.ZipEntry useEntry, byte[] data) 
		{
			if (File.Exists(path)) 
			{
				try 
				{
					File.Delete(path);
				}
				catch (Exception e)
				{
					throw(new ApplicationException("Cannot delete file: " + path + ". Please, check file system permissions!", e.InnerException)); 
				}
			}

			if (path.IndexOf("\\") > -1) 
			{
				Directory.CreateDirectory(path.Substring(0,path.LastIndexOf("\\")));
			}

			using (System.IO.FileStream streamWriter = File.Open(path,System.IO.FileMode.Create,System.IO.FileAccess.Write,System.IO.FileShare.None)) 
			{
				if (data != null) 
				{
					streamWriter.Write(data, 0, data.Length);
				} 
				else 
				{
					streamWriter.Close();

					throw(new Exception("Compress file " + useEntry.Name + " is 0 byte length!")); 
				}

				streamWriter.Close();

				return true;
			}
		}

		private string MatchFileName(string name, XmlNodeList nodeList) 
		{ 
			System.Text.RegularExpressions.Regex regex = new System.Text.RegularExpressions.Regex(@"("+ name.Replace(".","\\.").ToLower() + ")\\z");

			foreach (XmlNode node in nodeList)
			{   
				if (regex.IsMatch(node.InnerText.ToLower())) 
				{
					return node.InnerText;
				}
			}
			
			return null;
		}

		private string MatchFileName(string name) 
		{ 
			regex = new System.Text.RegularExpressions.Regex(@"("+ name.Replace(".","\\.").ToLower() + ")\\z");

			foreach (System.Web.UI.Pair pair in entries) 
			{
				ZipEntry entry = (ZipEntry)pair.First;

				if (regex.IsMatch(entry.Name.ToLower())) 
				{
					return name;
				}
			}
			
			return null;
		}

		private string FindNode(string nodeName) 
		{
			XmlNode xmlNode = xmlDoc.SelectSingleNode(nodeName);

			if (xmlNode == null) 
			{
				return "";
			}

			return xmlNode.InnerText;
		}
	}

	class FormletInfo 
	{
		private string mName;
		private string mAssembly;
		private string mControl;
		private string mDefinition;
		private string mConfigFile;
		private string mControlAscx;
		private string mDefinitionAscx;
		private string mFolder;
		private string mNamespace;
		private XmlNodeList mDependencies;
		private XmlNodeList mResources;

		public string Name 
		{
			get 
			{
				return mName;
			}
			set
			{
				mName = value;
			}
		}

		public string Assembly 
		{
			get 
			{
				return mAssembly;
			}
			set
			{
				mAssembly = value;
			}
		}

		public string Control 
		{
			get 
			{
				return mControl;
			}
			set
			{
				mControl = value;
			}
		}

		public string Definition 
		{
			get 
			{
				return mDefinition;
			}
			set
			{
				mDefinition = value;
			}
		}

		public string ConfigFile 
		{
			get 
			{
				return mConfigFile;
			}
			set
			{
				mConfigFile = value;
			}
		}

		public string Folder 
		{
			get 
			{
				return mFolder;
			}

			set 
			{
				mFolder = value;
			}

		}

		public string ControlAscx 
		{
			get 
			{
				return mControlAscx;
			}

			set 
			{
				mControlAscx = value;
			}

		}

		public string DefinitionAscx 
		{
			get 
			{
				return mDefinitionAscx;
			}

			set 
			{
				mDefinitionAscx = value;
			}

		}

		public string Namespace 
		{
			get 
			{
				return mNamespace;
			}

			set 
			{
				mNamespace = value;
			}
		}
		public XmlNodeList Dependencies 
		{
			get 
			{
				return mDependencies;
			}
			set
			{
				mDependencies = value;
			}
		}

		public XmlNodeList Resources 
		{
			get 
			{
				return mResources;
			}
			set
			{
				mResources = value;
			}
		}
	}
}
